#include "cProgramOptions.h"

cProgramOptions::cProgramOptions(void)
{
    // Initialize the member variables to their default values
    setDefaults();
}

cProgramOptions::cProgramOptions(int argc, _TCHAR *const *argv)
{

    bool OptionByCommandLine[129];
    for (int i = 0; i<128; i++)
        OptionByCommandLine[i] = false;

    // Initialize the member variables to their default values
    setDefaults();

	// Define the available options
	struct option long_options[] =
	{
		// {char*   name,                int has_arg, int* flag,    int val}
		{(_TCHAR *)"brief",				 no_argument,		0,	'b'},
		{(_TCHAR *)"database-directory", required_argument,	0,	'd'},
		{(_TCHAR *)"report-every",		 required_argument,	0,	'e'},
		{(_TCHAR *)"generate-pattern",	 no_argument,		0,	'g'},
		{(_TCHAR *)"help",				 no_argument,		0,	'h'},
		{(_TCHAR *)"window-length",		 required_argument,	0,	'l'},
		{(_TCHAR *)"modulus",			 required_argument,	0,	'm'},
		{(_TCHAR *)"database-name",		 required_argument,	0,	'n'},
		{(_TCHAR *)"overwrite-database", no_argument,		0,	'o'},
		{(_TCHAR *)"pattern",			 required_argument,	0,	'p'},
		{(_TCHAR *)"radix",				 required_argument,	0,	'r'},
		{(_TCHAR *)"search",			 no_argument,		0,	's'},
		{(_TCHAR *)"path-to-text-file",	 required_argument,	0,	't'},
		{(_TCHAR *)"verbose-file",		 required_argument,	0,	'v'},
		{(_TCHAR *)"write",				 no_argument,		0,	'w'},
		{0, 0, 0, 0}
	};
    // build the string of short options and convert to type consistant with argument 
    //string temp = bldShortOptions(long_options);
    //const _TCHAR * shortOptions = temp.c_str();

	// getopt_long stores the option index here.
	int option_index = 0;
	int optionCode;
	while (1)
	{
optionCode = getopt_long_only (argc, argv, (_TCHAR *)"bd:e:hl:m:n:op:r:st:v:w", long_options, &option_index);
//		optionCode = getopt_long_only (argc, argv, shortOptions, long_options, &option_index);
		// Detect the end of the options.
		if (optionCode != -1)
            parseOptions(true, optionCode, optarg, OptionByCommandLine);
        else
 			break;
	}
    // Check to see if there is a config file; and if so, get the additional parameters
    readConfigFile(OptionByCommandLine);

}

cProgramOptions::~cProgramOptions(void)
{
	// free the memory holding our strings
	if (dbName_ != "")
		free(dbName_);
    if (rfName_ != "")
        free(rfName_);
	if (dbDirectory_ != "")
		free(dbDirectory_);
	if (tfPath_ != "")
		free(tfPath_);
	if (sPattern_ != "")
		free(sPattern_);
}

void cProgramOptions::readConfigFile(bool OptionByCommandLine[])
{
    // build the file name from the input parameters
    ostringstream fName;
    fName << dbDirectory_ << dbName_ << ".config\0";
	ifstream configFile(fName.str());
	if(!configFile.is_open())
		printf("\nNo config file found with name (%s).\n Therefore, only cmd-line options will be used.\n", fName.str());
	else
    {
		printf("A config file was found  with name (%s).\n Therefore, the following options were added:\n", fName.str());
        string inBuffer;
        size_t bufLen, fromOff, optLen;
        while(getline(configFile, inBuffer))
        {
            bufLen = inBuffer.length();                                                                             
            char optionLetter = inBuffer[1];
            if (inBuffer[2] == '=')
            {
                if (inBuffer[3] == '\"')
                {
                    fromOff = 4;
                    optLen = bufLen - 5;
                }
                else
                {
                    fromOff = 3;
                    optLen = bufLen - 3;
                }
                    
                string opt = inBuffer.substr(fromOff, optLen);
                const _TCHAR * optArg = static_cast<const _TCHAR *>(opt.c_str());
                parseOptions(false, optionLetter, optArg, OptionByCommandLine);
            }
            else
            {
                const _TCHAR * optArg = NULL;
                parseOptions(false, optionLetter, optArg, OptionByCommandLine);
            }
 
        }

        configFile.close();
    }
}

void cProgramOptions::setDefaults(void)
{
	dbName_         = "";
 	dbDirectory_    = "";
	tfPath_         = "";
    rfName_         = "";
	sPattern_       = "";
	radix_			= 0;
	modulus_		= 0;
	windowSize_		= 0;
	mbPerTime_		= 1;
	isGenerate_	    = false;
	isVerbose_	    = false;
	isHelp_		    = false;
	isWrite_		= false;
	isReplace_	    = false;
	isSearch_   	= false;
	isVerify_   	= true;
}


void cProgramOptions::setDefaultModulus(void)
{
    if (radix_ <= 2)
        modulus_ = R2_MOD_MAX;
    else if (radix_ <= 4) 
        modulus_ = R4_MOD_MAX;   
    else if (radix_ <= 8) 
        modulus_ = R8_MOD_MAX;   
    else if (radix_ <= 16) 
        modulus_ = R16_MOD_MAX;    
    else if (radix_ <= 32) 
        modulus_ = R32_MOD_MAX;    
    else if (radix_ <= 48) 
        modulus_ = R48_MOD_MAX;    
    else if (radix_ <= 64) 
        modulus_ = R64_MOD_MAX;    
    else if (radix_ <= 128) 
        modulus_ = R128_MOD_MAX;    
    else if (radix_ <= 160) 
        modulus_ = R160_MOD_MAX;    
    else if (radix_ <= 256) 
        modulus_ = R256_MOD_MAX;    
    return;
}

void cProgramOptions::helpOptions(void)
{
    printf("\n\n USAGE:\n");
    printf("--brief (or -b)                flag to suppres verfication step in search.\n");
	printf("--database-directory= (or -d=) path to the index database.\n");
	printf("--report-every= (or -e=)       Mb to report time statistics secs/X Mb\n");
	printf("--generate-pattern (or -g)     flag to randomly select pattern from text file.\n");
	printf("--help (or -h)                 flag to show list of command-line arguments.\n");
	printf("--window-length=(or -l=)       length of window for creating fingerprints.\n");
	printf("--modulus= (or -m=)            prime number for fingerprint modulo arithmatic.\n");
	printf("--database-name= (or -n=)      name of the index database\n");
	printf("--overwrite-database (or -o)   flag to replace index database with a new one.\n");
	printf("--pattern= (or -p=)            pattern to search for.\n");
	printf("--radix= (or -r=)              radix to calculate fingerprints with.\n");
	printf("--search (or -s)               flag to signal a search is (also) requested.\n");
	printf("--path-to-text-file= (or -t=)  path & name of text file to index.\n");
	printf("--verbose-file= (or -v=)       path & name of text file to append perf. data.\n");
	printf("--write (or -w)                flag to write or append to index database.\n\n");
	printf("NOTE-1) Always use '/' as the path separator for both DOS & UNIX; and, \n");
	printf("        DO NOT place a '/' at the end of the path (e.g., \"C:/dir1/dir2\").\n\n");
	printf("NOTE-2) All options (except -d & -n) can be set in a configuration text file \n"); 
    printf("        as follows: the program will search for and use a file with the name:\n"); 
    printf("        [--database-directory]+[/]+[--database-name]+[.config] if it exists. \n"); 
	printf("        Use only short option names in the file, supplying one option per line.\n");
	printf("        Do not indent lines, ensuring the \"-\" is the first char on a line.\n");
    printf("        For example, to set the radix to 128 use \"-r=128\" on a separate \n");
    printf("        line. Also, options supplied on the command-line take precidence \n");
    printf("        over options given in the config file. In other words, with the \n");
    printf("        example given above for radix, the value of 128 will only be used\n");
    printf("        if the \"--radix=\" (or \"-r=\") option IS NOT supplied on the \n");
    printf("        command-line. \n");
	return ;    
}

void cProgramOptions::parseOptions(bool isCmdLine, int pOption, const _TCHAR* pOptArg, bool OptionByCommandLine[])
{
	switch (pOption)
	{
		case 'b':
            if (isCmdLine) 
                OptionByCommandLine['b'] = true;
            else if (OptionByCommandLine['b'])
            {
 			   printf ("-b in config file was set on command-line & will be ignored\n");
               break;
            }
            printf ("option -b is set (will verify fingerprint matches) \n");
			isVerify_ = false;
			break;
		case 'd':
            if (isCmdLine) 
                OptionByCommandLine['d'] = true;
            else if (OptionByCommandLine['d'])
            {
 			   printf ("-d in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -d is set (database directory is: `%s' )\n", pOptArg);
		    dbDirectory_ = (char *) malloc(strlen(pOptArg)+1);
		    memcpy(dbDirectory_,pOptArg,strlen(pOptArg)+1);
			break;
		case 'e':
            if (isCmdLine) 
                OptionByCommandLine['e'] = true;
            else if (OptionByCommandLine['e'])
            {
 			   printf ("-e in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -e is set (time reported as seconds / `%s' Mb)\n", pOptArg);
			mbPerTime_ = atoi(pOptArg);
			break;
		case 'g':
            if (isCmdLine) 
                OptionByCommandLine['g'] = true;
            else if (OptionByCommandLine['g'])
            {
 			   printf ("-g in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -g is set (a random pattern will be selected from text file)\n");
			isGenerate_ = true;
			break;
		case 'h':
            if (isCmdLine) 
                OptionByCommandLine['h'] = true;
            else if (OptionByCommandLine['h'])
            {
 			   printf ("-h in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -h is set (command-line option help will be given)\n");
			isHelp_ = true;
			break;
		case 'l':
            if (isCmdLine) 
                OptionByCommandLine['l'] = true;
            else if (OptionByCommandLine['l'])
            {
 			   printf ("-l in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -l is set (window length of `%s' bytes)\n", pOptArg);
			windowSize_ = atoi(pOptArg);
			break;
		case 'm':
            if (isCmdLine) 
                OptionByCommandLine['m'] = true;
            else if (OptionByCommandLine['m'])
            {
 			   printf ("-m in config file was set on command-line & will be ignored\n");
               break;
            }
			modulus_ = atoi(pOptArg);
            if (modulus_ != 0)
			    printf ("option -m is set (modulus of `%s' will be used)\n", pOptArg);
			break;
		case 'n':
            if (isCmdLine) 
                OptionByCommandLine['n'] = true;
            else if (OptionByCommandLine['n'])
            {
 			   printf ("-n in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -n is set (database name is `%s' )\n", pOptArg);
			dbName_ = (char *) malloc(strlen(pOptArg)+1);
			memcpy(dbName_,pOptArg,strlen(pOptArg)+1);
			break;
		case 'o':
            if (isCmdLine) 
                OptionByCommandLine['o'] = true;
            else if (OptionByCommandLine['o'])
            {
 			   printf ("-o in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -o is set (database will be over-written)\n");
			isReplace_ = true;
			break;
		case 'p':
            if (isCmdLine) 
                OptionByCommandLine['p'] = true;
            else if (OptionByCommandLine['p'])
            {
 			   printf ("-p in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -p is set (searching for pattern `%s' )\n", pOptArg);
			sPattern_ = (char *) malloc(strlen(pOptArg)+1);
			memcpy(sPattern_,pOptArg,strlen(pOptArg)+1);
			break;
		case 'r':
            if (isCmdLine) 
                OptionByCommandLine['r'] = true;
            else if (OptionByCommandLine['r'])
            {
 			   printf ("-r in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -r is set (radix value of `%s' will be used)\n", pOptArg);
			radix_ = atoi(pOptArg);
			break;
		case 's':
            if (isCmdLine) 
                OptionByCommandLine['s'] = true;
            else if (OptionByCommandLine['s'])
            {
 			   printf ("-s in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -s is set (a search will be conducted)\n");
			isSearch_ = true;
			break;
		case 't':
            if (isCmdLine) 
                OptionByCommandLine['t'] = true;
            else if (OptionByCommandLine['t'])
            {
 			   printf ("-t in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -t is set (text file used will be `%s' )\n", pOptArg);
			tfPath_ = (char *) malloc(strlen(pOptArg)+1);
			memcpy(tfPath_,pOptArg,strlen(pOptArg)+1);
			break;
		case 'v':
            if (isCmdLine) 
                OptionByCommandLine['v'] = true;
            else if (OptionByCommandLine['v'])
            {
 			   printf ("-v in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -v is set (performance data appended to %s)\n", pOptArg);
			isVerbose_ = true;
			rfName_ = (char *) malloc(strlen(pOptArg)+1);
			memcpy(rfName_,pOptArg,strlen(pOptArg)+1);
			break;
		case 'w':
            if (isCmdLine) 
                OptionByCommandLine['w'] = true;
            else if (OptionByCommandLine['w'])
            {
 			   printf ("-w in config file was set on command-line & will be ignored\n");
               break;
            }
			printf ("option -w is set (database will be created or appended)\n");
			isWrite_ = true;
			break;
		case '?':
			// getopt_long already printed an error message.
			printf ("invalid option provided (program will be aborted)\n");
			exit(-1);
		default:
			abort();
	}
    return;
}

string cProgramOptions::bldShortOptions(const struct option long_options[])
{
    string temp = "";
    int i=0;
    while ((TCHAR)'a' <= long_options[i].val  && long_options[i].val <= (TCHAR)'z')
    {
        temp += long_options[i].val;
        if (long_options[i].has_arg == required_argument)
            temp += ':';
        printf("in build short");
printf("temp is now = %s /n", temp);
        i++;
    }
    return temp;
}

void cProgramOptions::verifyOptions(void)
{
    bool errorsFound = false;

    if (isWrite_ && strlen(tfPath_)==0)
    {
        errorsFound = true;
        printf("No text file from which to build index.\n");
    }
    if (isVerbose_&&strlen(rfName_)==0)
    {
        errorsFound = true;
        printf("No result file name given.  Verbose needs a result file.\n");
    }
    if (isSearch_) 
    {
        if (windowSize_==0)
        {
            errorsFound = true;
            printf("No window size given.  Search option needs the size of the window used when building the index.\n");
        }
        else if (!isGenerate_ && strlen(sPattern_)!=windowSize_)
        {
            errorsFound = true;
            printf("No pattern given. Search option needs a pattern to search for.\n");
        }
    }
    if (radix_ <= 0 || radix_ >= 256)
    {
        errorsFound = true;
        printf("Radix is out of range.  A radix between 2 and 128 is required.\n");
    }
    if (modulus_ == 0)
    {
        setDefaultModulus();
        printf("Modulus value not declared.  A default value of %d has been set.\n", modulus_);

    }
    long long testValue = radix_ * modulus_;
    if (modulus_ <= 0 || modulus_ >= R2_MOD_MAX || testValue > LONG_MAX)
    {
        errorsFound = true;
        printf("Modulus is out of range. The (radix * modulus) must be between 0 and %d\n", LONG_MAX);
    }
    if (modulus_ % 2 == 0)
    {
        errorsFound = true;
        printf("Modulus is an even value.  Modulus must be a prime number.\n");
    }
    if (!isWrite_ && !isSearch_)
    {
        errorsFound = true;
        printf("No Write or Search instruction given.\n");
    }
    if (!isWrite_ && isVerify_)
    {
        errorsFound = true;
        printf("Verify option was given without a 'write' instruction.\n");
    }
    if (errorsFound)
        throw " Parameters are incomplete or conflicting. Processing cannot continue.\n";

    return ;
}

